package edu.columbia.aicheckers.agent;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.Set;

import edu.columbia.aicheckers.model.Color;
import edu.columbia.aicheckers.model.IBoardState;
import edu.columbia.aicheckers.model.Move;

/**
 * This is a rules based agent that has the following rules in 
 * terms of priority and the current state of the game:
 *  
 * 1. See if the available moves contains a move that will allow 
 * a skip and not allow a skip for the opponent - if so - take it
 * 2. See if there is a move that defensively doesn't allow any skip moves for the opponent - take it
 * 3. See if there is a move that allows a skip - if so - take it
 * 4. Check if agent is making the same moves as it did in the past - if so - choose a new random move to advance state of game
 * Note: Any move within these 3 rules that makes a checker king takes precedence (within the rule) 
 * 
 * 
 * @author C.M.R
 *
 */
public class RuleBasedAgent implements IAgent{

	Random rand = new Random();
	private Queue<Move> pastMoves = new LinkedList<Move>();
	
	public Move doPly(IBoardState board, Color myColor) {
		Set<Move> moves = board.getAvailableMoves(myColor);
		if(!moves.isEmpty())
		{
			Move m = analyzeMoves(board, moves, myColor);
			pastMoves.add(m);
			if(pastMoves.size() > 4)
			{
				pastMoves.remove();
			}
			return m;
		}
		else
		{
			return null;
		}
	}
	
	/**
	 * Examines possible moves based on the above rules and returns the chosen move
	 * @param currentState
	 * @param moves
	 * @param myColor
	 * @return
	 */
	private Move analyzeMoves(IBoardState currentState, Set<Move> moves, Color myColor)
	{
		HashSet<Move> skipMoves = new HashSet<Move>();
		HashSet<Move> nonSkipMoves = new HashSet<Move>();
		
		// Get all the skip moves
		for(Move m : moves)
		{
			if(m.hasSkip())
			{
				skipMoves.add(m);
			}
			else
			{
				nonSkipMoves.add(m);
			}
		}
		
		Move moveToTake = null;
		// Rule 3
		for(Move skipMove : skipMoves)
		{
			// Just skip something
			return skipMove;
		}
		
		// Rule 2
		for(Move nonSkipMove : nonSkipMoves)
		{
			IBoardState nextState = currentState.simulateMove(nonSkipMove);
			Set<Move> nextMoves = nextState.getAvailableMoves(Color.getOponnentColor(myColor));
			if(!hasSkip(nextMoves))
			{
				//Take move
				if(moveToTake == null)
				{
					moveToTake = nonSkipMove;
				}
				else if(nonSkipMove.makeNewKing())
				{
					moveToTake = nonSkipMove;
				}
			}
		}
		
		// Rule 1
		for(Move skipMove : skipMoves)
		{
			IBoardState nextState = currentState.simulateMove(skipMove);
			if(nextState.getCurrentPly() != myColor)
			{
				
				Set<Move> nextMoves = nextState.getAvailableMoves(Color.getOponnentColor(myColor));
				if(!hasSkip(nextMoves))
				{
					//Ideal move
					if(moveToTake == null)
					{
						moveToTake = skipMove;
					}
					else if(skipMove.makeNewKing())
					{
						moveToTake = skipMove;
					}
					
				}
			}
			else
			{
//				 Very ideal move - double skip
				if(moveToTake == null)
				{
					moveToTake = skipMove;
				}
				else if(skipMove.makeNewKing())
				{
					moveToTake = skipMove;
				}
				break;
			}
			
		}
		
		// Need to check if agent is moving back and forth on the same move
		if(moveToTake != null && !isRedundantMove(moveToTake))
			return moveToTake;
		else
		{
			// No Ideal moves really - return a random move
			int picked = rand.nextInt(moves.size());
			int count = 0;
			Move m = null;
			Iterator<Move> movesIterator = moves.iterator();
			while(movesIterator.hasNext())
			{
				m = movesIterator.next();
				if(picked == count)
				{
					moveToTake = m;
				}
				count++;
			}
		}
		return moveToTake;
	}
	
	/**
	 * Checks for moves that are redundant
	 * @param moveToTake Checks to see if this move was done for the last move
	 * @return
	 */
	private boolean isRedundantMove(Move moveToTake) {
		for(Move m : pastMoves )
		{
			if(moveToTake.getCheckerPiece() == m.getCheckerPiece() 
					&& moveToTake.getDestination() == m.getDestination())
			{
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Checks to see if the available moves contains a move that skips
	 * If this is true, then the agent/user must choose from a list of moves
	 * that skip
	 * @param moves
	 * @return
	 */
	private static boolean hasSkip(Set<Move> moves) {
		
		for(Move m: moves)
		{
			if(m.hasSkip())
			{
				return true;
			}
			
		}
		return false;
	}
}
